package com.tx.core.mybatis.automapper;

import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.tx.core.exceptions.SILException;
import com.tx.core.starter.component.ComponentProperties;
import com.tx.core.util.TableInfoUtils;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * <pre>
 * 功能简述: 动态的Mapper注入实现
 * </pre>
 *
 * @author PengQingyang
 * 创建时间 [2024/3/26]
 */
@Slf4j
@Configuration
@AutoConfigureAfter(SpringUtil.class)
@EnableConfigurationProperties(ComponentProperties.class)
public class AutoMPMapperRegister implements InitializingBean {

    /** mapperMap */
    public final static Map<Class<?>,AutoMPMapper> mapperMap = new HashMap<>();

    /**
     * getMapper
     * @param entityClazz
     * @return
     */
    public static AutoMPMapper getMapper(Class<?> entityClazz){
        return mapperMap.get(entityClazz);
    }

    private  ComponentProperties propertis;

    /** sqlSessionFactory */
    @Resource
    private SqlSessionFactory sqlSessionFactory;

    /** basePackage */
    private String basePackages = "com.tx";

    /** <默认构造函数> */
    public AutoMPMapperRegister(ComponentProperties propertis) {
        this.propertis = propertis;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.basePackages = this.propertis.getMybatisAutoBasePackage();
    }

    /**
     * 启动期间装载类
     */
    @PostConstruct
    public void setup() {
        //String BASE_PATH = getClass().getPackage().getName().replace(".config", "");
        // 扫描数据库实体，根据实体创建对应 mapper、service
        //ClassUtil.scanPackageByAnnotation()
        Set<Class<?>> entityClassSet = new HashSet<>();
        String[] packages = StringUtils.splitByWholeSeparator(this.basePackages,";");
        for(String packageTemp : packages){
            if(StringUtils.isEmpty(packageTemp) || StringUtils.isBlank(packageTemp)){
                continue;
            }
            entityClassSet.addAll(ClassUtil.scanPackageByAnnotation(packageTemp, com.tx.core.mybatis.annotation.AutoMPMapper.class));
        }
        if (entityClassSet.isEmpty()) {
            return;
        }

        //发现存在相关实体类
        for (Class<?> entityClass : entityClassSet) {
            // 只创建带有 MapAndService 注解的实体

            com.tx.core.mybatis.annotation.AutoMPMapper ampAnno = entityClass.getAnnotation(com.tx.core.mybatis.annotation.AutoMPMapper.class);
            if (ampAnno == null) {
                continue;
            }
            //ClassLoaderUtil.isPresent()

            //ClassUtil.get
            String packageName = entityClass.getPackage().getName();
            //获取模块包名
            String modulePackage = packageName.substring(0, packageName.lastIndexOf("."));
            //判断需要生成的类是否存在
            if(ClassLoaderUtil.isPresent(modulePackage + ".dao." + entityClass.getSimpleName() + "Mapper") ||
                    ClassLoaderUtil.isPresent(modulePackage + ".mapper." + entityClass.getSimpleName() + "Mapper") ||
                    ClassLoaderUtil.isPresent(modulePackage + ".dao." + entityClass.getSimpleName() + "Dao")){
                //如果指定实体类的Mapper,Dao类存在，则不进行生成
                continue;
            }
            if(mapperMap.containsKey(entityClass)){
                continue;
            }
            //狗i按类名
            String mapperClassName = modulePackage + ".mapper" + "." + entityClass.getSimpleName() + "Mapper";
            //创建mapper
            Class<?> entityPkClass = TableInfoUtils.parsePKTableColumn(entityClass).getTypeDescriptor().getType();
            //构建类
            Class<?> mapperClass = new ByteBuddy()
                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(AutoMPMapper.class,
                            entityClass,entityPkClass).build())
                    .name(mapperClassName)
                    .make()
                    .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getLoaded();

            //构建完成后，向spring容器中注册该类的实例
            MapperFactoryBean<?> factoryBean = new MapperFactoryBean<>(mapperClass);
            factoryBean.setSqlSessionFactory(sqlSessionFactory);
            sqlSessionFactory.getConfiguration().addMapper(mapperClass);
            try {
                AutoMPMapper mp = (AutoMPMapper)factoryBean.getObject();
                SpringUtil.registerBean(getBeanName(mapperClassName), mp);
                mapperMap.put(entityClass,mp);
                log.info("register mapper Bean -> name:{}", getBeanName(mapperClassName));
            } catch (Exception e) {
                throw new SILException("自动为具备@AutoMPMapper的Entity类注册MybatisBaseMapper的子类",e);
            }
        }
    }

    /**
     * 生成bean名
     * @param className
     * @return
     */
    private String getBeanName(String className) {
        int index = className.lastIndexOf(".");
        String simpleClassName = index != -1 ? className.substring(index + 1) : className;
        return StringUtils.uncapitalize(simpleClassName);
    }
}
